// Copyright (c) 2022.
// Written by Li Yanxiao. All rights reserved

#ifndef SIGNED_DISTANCE_FIELD_DISTANCE_H
#define SIGNED_DISTANCE_FIELD_DISTANCE_H

#include <Eigen/Dense>
#include "Line.h"


/*
 * 返回一个点是否在一条线的起点的垂线外侧
 * 通过计算这个点与线段起点的向量 与 这条线段的向量的点乘符号得到
 * @returns: 布尔值，是否在垂线外侧
 * @params point: 待计算的点
 * @params Line: 待计算的线
 */
bool OverStart(const Eigen::Vector2i& point, const Line & line);

/*
 * 返回一个点是否在一条线终点垂线外侧
 * 通过计算这个点与线段终点的向量 与 这条线段的向量的点乘符号得到
 * @returns: 布尔值，是否在垂线外侧
 * @params point: 待计算的点
 * @params Line: 待计算的线
 */
bool OverEnd(const Eigen::Vector2i& point, const Line & line);

/*
 * 计算一个点到一条直线的距离,使用的是点到直线距离公式：
 * @returns: 点到直线的距离
 * @params point: 待计算的点
 * @params Line: 待计算的线
 */
inline double DistancePointToLine(const Eigen::Vector2i& point, const Line & line);

/*
 * 计算一个点到另一个点的距离，直接用勾股定理算
 * @returns: 两点间的距离
 * @params point1: 待计算的一个点
 * @params point2: 待计算的另一个点
 * 注：point1,point2可以位置互换，没有任何区别
 */
inline double DistancePointToPoint(const Eigen::Vector2i& point1, const Eigen::Vector2i& point2);

/*
 * 计算一个点到一条线段的最近距离
 * 如果这个点在线段左侧外或者右侧外，结果将是这个点到线段左端点/右端点的距离
 * 如果这个点在线段上/下方，结果将是这个点到线段的垂线段的长度
 */
double DistancePointToLineSegment(const Eigen::Vector2i& point, const Line & line);

/*
 * 计算一个点到三条线段构成的折线段的最短距离
 * 最终返回的结果是带符号的
 * 规定：
 *  线段以起点为中心，逆时针扫180度，点在这样的平面内时距离为正，其他为负
 */
double MinDistance(const Eigen::Vector2i& point, const Line line[]);


/*
 * 计算一个点到三条线段构成的折线段的距离最短时，这条线的另一个端点在哪
 */
Eigen::Vector2i* MinPoint(Eigen::Vector2i* dst, const Eigen::Vector2i& point, const Line line[]);

/*
 * 计算一个平面（长从0到cols，高从0到rows)的范围内每一个点到直线的距离
 */
Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>* MinDistanceList(Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>* dst,
                                                                       const Line line[],
                                                                       int rows=400, int cols = 600);

bool Cross2(Eigen::Vector2i vec1, Eigen::Vector2i vec2);

#endif //SIGNED_DISTANCE_FIELD_DISTANCE_H